package cn.lunatic.db;

import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import org.jdom.Content;
import org.jdom.Element;
import org.jdom.Text;

import cn.lunatic.bean.SqlStatement;
import cn.lunatic.cache.Config;
import cn.lunatic.core.Constant;
import cn.lunatic.pub.DataMap;
import cn.lunatic.utils.StringUtils;

/**
 * @Description:
 * @author ganfeng
 * @date 2014-3-6 下午04:37:41
 */
public class SQLParseUtil {
	
	static final String LUNATIC_SQL_MAPS_SELECT = "/lunatic/sqls/query[@id='";
	static final String LUNATIC_SQL_MAPS_UPDATE = "/lunatic/sqls/update[@id='";
	static final String SQL_MAPS_CONDITION_ELEMENT = "isExist,isNotExist,isEmpty,isNotEmpty";
	static final String TORUK_SQL_MAPS_PREPEND = "prepend";
	static final String TORUK_SQL_MAPS_PROPERTY = "property";
	static final String SQL_MAPS_TEXT_ELEMENT = "text";

	/**
	 * 
	 * @Description:
	 * @author ganfeng
	 * @date 2014-3-10 上午11:19:45
	 * @param sqlId
	 * @param params
	 * @param type
	 * @return
	 */
	public static SqlStatement parseSQLBySqlID(String sqlId, DataMap params, String type){
		String path = "";
		if(Constant.SQL_TYPE_QUERY.equalsIgnoreCase(type)){
			path = LUNATIC_SQL_MAPS_SELECT + sqlId;
		}
		if(Constant.SQL_TYPE_UPDATE.equalsIgnoreCase(type)){
			path = LUNATIC_SQL_MAPS_UPDATE + sqlId;
		}
		
		Element element = Config.xpathGetNode(path + Config.XPATHATTR_END);
		
		SqlStatement statement = getStatement(element, params);
		statement.setSqlId(sqlId);
		return statement;
	}
	
	private static SqlStatement getStatement(Element sqlElement, DataMap params){
		String sqlText = getSqlText(sqlElement, params, "");
		SqlStatement ret = new SqlStatement();
		ret.setSql(sqlText);

		String sql = sqlText;
		String regEx = "#([^#]*)#";
		Pattern p = Pattern.compile(regEx);
		Matcher m = p.matcher(sqlText);
		String exp = "";
		while (m.find()) {
			exp = m.group(0);
			String paramName = getSQLParamName(exp);// 获取变量名
			String paramValue = params.getString(paramName);// 获取变量值
			sql = sql.replaceFirst(exp, "?");
			ret.addParam(paramValue);
		}
		ret.setSql(sql);
		return ret;
	}
	
	/**
	 * 
	 * @Description: 通过配置文件的参数配置获取参数名称
	 * @author ganfeng
	 * @date 2014-3-7 下午03:12:47
	 * @param paramEXP
	 *            参数表达式，形如：#参数名#格式
	 * @return
	 */
	private static String getSQLParamName(String paramEXP) {
		if (paramEXP == null) {
			return null;
		}
		return paramEXP.replaceAll("#", "");// 去除表达式的#
	}
	
	/**
	 * 
	 * @Description:通过配置文件的参数配置获取参数值
	 * @author ganfeng
	 * @date 2014-3-7 下午05:52:36
	 * @param paramNameEXP 参数名称表达式，形如：#参数名#格式
	 * @param params 参数集合
	 * @return
	 */
	private static String getSQLParamValue(String paramNameEXP , DataMap params) {
		String paramName = getSQLParamName(paramNameEXP);
		if (paramName == null) {
			return null;
		}
		return params.getString(paramName);
	}

	/**
	 * 
	 * @Description:通过参数获取sql语句---递归获取
	 * @author ganfeng
	 * @date 2014-3-7 下午02:42:42
	 * @param sqlElement
	 *            sql语句的配置文件节点
	 * @param params
	 *            数据库执行参数
	 * @param parentPrepend
	 *            父prepend
	 * @return
	 */
	@SuppressWarnings("unchecked")
	private static String getSqlText(Content sqlElement, DataMap params, String parentPrepend) {
		String retSqlText = "";
		if (sqlElement instanceof Element) { // 是元素，解释该元素下面的所有子元素
			List<Content> children = ((Element) sqlElement).getContent();
			boolean first = true;
			String temp = "";
			for (Content child : children) {
				if (child instanceof Element) {
					Element childElement = (Element) child;
					String name = childElement.getName();
					if (SQL_MAPS_CONDITION_ELEMENT.indexOf(name) > -1) {
						if (!isMatching(childElement, params)) {
							continue;
						}
					}
					String prepend = childElement.getAttributeValue(TORUK_SQL_MAPS_PREPEND);
					prepend = (prepend == null ? "" : " " + prepend);
					if (first) {// 当前元素是第一个元素
						if (!StringUtils.isBlank(parentPrepend)) {
							prepend = parentPrepend + " ";
						}
						parentPrepend = "";
					} else {// 当前元素非第一个元素
						if (!StringUtils.isBlank(prepend)) {// prepend不为空，两端加空格
							prepend = " " + prepend + " ";
						} else {// prepend为空，设置为空字符串
							prepend = "";
						}
					}
					if (SQL_MAPS_TEXT_ELEMENT.equals(name)) {// 如果是文本元素，直接从使用变量内容
						String text = childElement .getAttributeValue(TORUK_SQL_MAPS_PROPERTY);
						text = text.replaceAll("#", "");// 去除变量的#符号
						temp = params.getString(text);
					} else {
						temp = getSqlText(child, params, prepend); // 递归执行
					}
				} else if (child instanceof Text) {// 子元素是文本,直接获取文本
					temp = oneLine((String) ((Text) child).getText());
				}
				if (StringUtils.isBlank(temp)) {// 如果是空串，没有内容，忽略
					continue;
				}
				retSqlText += (first ? parentPrepend : "") + temp;
				first = false;
			}
		} else if (sqlElement instanceof Text) { // 是文本元素
			retSqlText = oneLine(((Text) sqlElement).getText());
		}
		return retSqlText;
	}

	private static boolean isMatching(Element element, DataMap params) {
		if (params == null) {
			return false;
		}
		boolean ret = false;
		String name = element.getName(); // 条件名
		String property = element.getAttributeValue(TORUK_SQL_MAPS_PROPERTY); // 判断的变量名
		if (StringUtils.isBlank(property)) {// 没有正确设置条件，返回不匹配
			return false;
		}
		String val = getSQLParamValue(property, params);// 获取变量值
		if ("isExist".equals(name) && val != null) {
			ret = true;
		} else if ("isNotExist".equals(name) && val == null) {
			ret = true;
		} else if ("isEmpty".equals(name) && "".equals(val)) {
			ret = true;
		} else if ("isNotEmpty".equals(name) && val != null && !"".equals(val)) {
			ret = true;
		}

		return ret;
	}

	/**
	 * 
	 * @Description:把有回车字符的字符串，分成多行，去掉两端所有空格， 再将多行串起来成一个字符串，中间用一个空格隔开。
	 * @author ganfeng
	 * @date 2014-3-6 下午06:01:17
	 * @param content
	 * @return
	 */
	public static String oneLine(String content) {
		if (StringUtils.isBlank(content)) {
			return "";
		}
		StringBuffer buf = new StringBuffer();
		boolean b = false;
		String tmp = "";
		for (int i = 0; i < content.length(); i++) {
			tmp = content.substring(i, i + 1);
			if ("\n\t ".indexOf(tmp) >= 0) {
				tmp = " ";
				if (!b) {
					b = true;
					buf.append(tmp);
				}
			} else {
				buf.append(tmp);
				b = false;
			}
		}
		String ret = buf.toString();
		return ret;
	}
}
